1use crate::{AviUtl2Info, generic::EditSection};
2use pastey::paste;
3use std::num::NonZeroIsize;
4
5pub struct HostAppHandle<'a> {
12 internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
13 global_leak_manager: &'a mut crate::common::LeakManager,
14 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
15 plugin_registry: &'a mut crate::generic::PluginRegistry,
16}
17
18pub struct SubPlugin<T> {
20 plugin: std::marker::PhantomData<T>,
21 internal: std::sync::Arc<InternalReferenceHandle>,
22}
23struct InternalReferenceHandle {
24 uninitialize_fn: fn(),
25}
26impl Drop for InternalReferenceHandle {
27 fn drop(&mut self) {
28 (self.uninitialize_fn)();
29 }
30}
31
32impl<'a> HostAppHandle<'a> {
33 pub(crate) unsafe fn new(
34 internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
35 global_leak_manager: &'a mut crate::common::LeakManager,
36 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
37 plugin_registry: &'a mut crate::generic::PluginRegistry,
38 ) -> Self {
39 Self {
40 internal,
41 global_leak_manager,
42 kill_switch,
43 plugin_registry,
44 }
45 }
46
47 fn assert_not_killed(&self) {
48 if self.kill_switch.load(std::sync::atomic::Ordering::SeqCst) {
49 panic!("This HostAppHandle is no longer valid.");
50 }
51 }
52
53 pub fn set_plugin_information(&mut self, information: &str) {
56 self.assert_not_killed();
57 let information = if cfg!(debug_assertions) {
58 format!("{information} (Debug Build)")
59 } else {
60 information.to_string()
61 };
62 unsafe {
63 ((*self.internal).set_plugin_information)(
64 self.global_leak_manager.leak_as_wide_string(&information),
65 )
66 }
67 }
68
69 pub fn create_edit_handle(&mut self) -> crate::generic::EditHandle {
71 self.assert_not_killed();
72 let raw_handle = unsafe { ((*self.internal).create_edit_handle)() };
73 unsafe { crate::generic::EditHandle::new(raw_handle) }
74 }
75
76 pub fn register_import_menu(
82 &mut self,
83 name: &str,
84 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
85 ) {
86 self.assert_not_killed();
87 unsafe {
88 ((*self.internal).register_import_menu)(
89 self.global_leak_manager.leak_as_wide_string(name),
90 callback,
91 )
92 }
93 }
94
95 pub fn register_export_menu(
101 &mut self,
102 name: &str,
103 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
104 ) {
105 self.assert_not_killed();
106 unsafe {
107 ((*self.internal).register_export_menu)(
108 self.global_leak_manager.leak_as_wide_string(name),
109 callback,
110 )
111 }
112 }
113
114 pub fn register_layer_menu(
121 &mut self,
122 name: &str,
123 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
124 ) {
125 self.assert_not_killed();
126 unsafe {
127 ((*self.internal).register_layer_menu)(
128 self.global_leak_manager.leak_as_wide_string(name),
129 callback,
130 )
131 }
132 }
133
134 pub fn register_object_menu(
141 &mut self,
142 name: &str,
143 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
144 ) {
145 self.assert_not_killed();
146 unsafe {
147 ((*self.internal).register_object_menu)(
148 self.global_leak_manager.leak_as_wide_string(name),
149 callback,
150 )
151 }
152 }
153
154 pub fn register_edit_menu(
161 &mut self,
162 name: &str,
163 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
164 ) {
165 self.assert_not_killed();
166 unsafe {
167 ((*self.internal).register_edit_menu)(
168 self.global_leak_manager.leak_as_wide_string(name),
169 callback,
170 )
171 }
172 }
173
174 pub fn register_config_menu(
181 &mut self,
182 name: &str,
183 callback: extern "C" fn(aviutl2_sys::plugin2::HWND, aviutl2_sys::plugin2::HINSTANCE),
184 ) {
185 self.assert_not_killed();
186 unsafe {
187 ((*self.internal).register_config_menu)(
188 self.global_leak_manager.leak_as_wide_string(name),
189 callback,
190 )
191 };
192 }
193
194 pub fn register_window_client<T: raw_window_handle::HasWindowHandle>(
200 &mut self,
201 name: &str,
202 instance: &T,
203 ) -> Result<(), raw_window_handle::HandleError> {
204 self.assert_not_killed();
205 let raw_handle = instance.window_handle()?;
206 let hwnd = match raw_handle.as_raw() {
207 raw_window_handle::RawWindowHandle::Win32(handle) => handle.hwnd,
208 _ => panic!("Only Win32WindowHandle is supported"),
209 };
210 unsafe {
211 ((*self.internal).register_window_client)(
212 self.global_leak_manager.leak_as_wide_string(name),
213 hwnd.get() as *mut std::ffi::c_void,
214 );
215 }
216 Ok(())
217 }
218
219 pub fn register_menus<T: GenericPluginMenus>(&mut self) {
225 self.assert_not_killed();
226 T::register_menus(self);
227 }
228
229 pub fn register_project_load_handler(
237 &mut self,
238 callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
239 ) {
240 self.assert_not_killed();
241 unsafe {
242 ((*self.internal).register_project_load_handler)(callback);
243 }
244 }
245
246 pub fn register_project_save_handler(
253 &mut self,
254 callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
255 ) {
256 self.assert_not_killed();
257 unsafe {
258 ((*self.internal).register_project_save_handler)(callback);
259 }
260 }
261
262 pub fn register_clear_cache_handler(
269 &mut self,
270 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
271 ) {
272 self.assert_not_killed();
273 unsafe {
274 ((*self.internal).register_clear_cache_handler)(callback);
275 }
276 }
277
278 pub fn register_change_scene_handler(
285 &mut self,
286 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
287 ) {
288 self.assert_not_killed();
289 unsafe {
290 ((*self.internal).register_change_scene_handler)(callback);
291 }
292 }
293}
294
295pub trait GenericPluginMenus {
304 fn register_menus(host: &mut HostAppHandle);
305}
306
307#[doc(inline)]
308pub use aviutl2_macros::generic_menus as menus;
309
310#[derive(Default)]
311pub(crate) struct PluginRegistry {
312 #[cfg(feature = "input")]
313 input_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
314 #[cfg(feature = "output")]
315 output_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
316 #[cfg(feature = "filter")]
317 filter_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
318 #[cfg(feature = "module")]
319 script_modules: Vec<std::sync::Arc<InternalReferenceHandle>>,
320}
321impl PluginRegistry {
322 pub(crate) fn new() -> Self {
323 Self::default()
324 }
325}
326
327macro_rules! impl_plugin_registry {
328 (
329 $description:literal,
330 $feature:literal,
331 $module:ident,
332 $name:ident,
333 $register_method:ident,
334 $PluginTrait:path,
335 $SingletonTrait:path,
336 $TableType:ty
337 ) => {
338 paste! {
339 impl<T> SubPlugin<T> {
340 #[cfg(feature = $feature)]
341 #[doc = concat!($description, "の新しいインスタンスを作成します。")]
342 pub fn [<new_ $name>](info: AviUtl2Info) -> crate::AnyResult<Self>
343 where
344 T: $PluginTrait + $SingletonTrait + 'static
345 {
346 crate::$module::__bridge::initialize_plugin::<T>(info.version.into())?;
347 let internal = std::sync::Arc::new(InternalReferenceHandle {
348 uninitialize_fn: || {
349 unsafe {
350 crate::$module::__bridge::uninitialize_plugin::<T>();
351 }
352 },
353 });
354 Ok(Self {
355 plugin: std::marker::PhantomData,
356 internal,
357 })
358 }
359 }
360 #[cfg(feature = $feature)]
361 impl<'a> HostAppHandle<'a> {
362 #[doc = concat!($description, "を登録します。")]
363 pub fn [<register_ $name>]<T: $PluginTrait + $SingletonTrait + 'static>(
364 &mut self,
365 handle: &SubPlugin<T>,
366 ) {
367 self.assert_not_killed();
368 unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table::<T>()) };
369 self.plugin_registry
370 .[<$name s>]
371 .push(std::sync::Arc::clone(&handle.internal));
372 }
373 }
374 }
375 };
376}
377
378impl_plugin_registry!(
379 "入力プラグイン",
380 "input",
381 input,
382 input_plugin,
383 register_input_plugin,
384 crate::input::InputPlugin,
385 crate::input::__bridge::InputSingleton,
386 aviutl2_sys::input2::INPUT_PLUGIN_TABLE
387);
388impl_plugin_registry!(
389 "出力プラグイン",
390 "output",
391 output,
392 output_plugin,
393 register_output_plugin,
394 crate::output::OutputPlugin,
395 crate::output::__bridge::OutputSingleton,
396 aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE
397);
398impl_plugin_registry!(
399 "フィルタープラグイン",
400 "filter",
401 filter,
402 filter_plugin,
403 register_filter_plugin,
404 crate::filter::FilterPlugin,
405 crate::filter::__bridge::FilterSingleton,
406 aviutl2_sys::filter2::FILTER_PLUGIN_TABLE
407);
408impl_plugin_registry!(
409 "スクリプトモジュール",
410 "module",
411 module,
412 script_module,
413 register_script_module,
414 crate::module::ScriptModule,
415 crate::module::__bridge::ScriptModuleSingleton,
416 aviutl2_sys::module2::SCRIPT_MODULE_TABLE
417);
418
419#[derive(Debug)]
421pub struct EditHandle {
422 pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
423}
424
425unsafe impl Send for EditHandle {}
426unsafe impl Sync for EditHandle {}
427
428#[derive(thiserror::Error, Debug)]
430pub enum EditHandleError {
431 #[error("api call failed")]
432 ApiCallFailed,
433}
434
435impl EditHandle {
436 pub(crate) unsafe fn new(internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE) -> Self {
437 Self { internal }
438 }
439
440 pub fn call_edit_section<'a, T, F>(&self, callback: F) -> Result<T, EditHandleError>
446 where
447 T: Send + 'static,
448 F: FnOnce(&mut EditSection) -> T + Send + 'a,
449 {
450 type CallbackParam<'a, F, T> = (ChildKillablePointer<Option<F>>, &'a mut Option<T>);
451
452 let closure = Some(callback);
453 let param = KillablePointer::new(closure);
454 let child_param = param.create_child();
455
456 extern "C" fn trampoline<F, T>(
457 param: *mut std::ffi::c_void,
458 edit_section: *mut aviutl2_sys::plugin2::EDIT_SECTION,
459 ) where
460 T: Send + 'static,
461 F: FnOnce(&mut EditSection) -> T,
462 {
463 unsafe {
464 let (child_param, result_ptr) = &mut *(param as *mut CallbackParam<F, T>);
465 let callback = child_param
466 .as_mut()
467 .take()
468 .expect("Callback has already been called");
469 let mut edit_section = EditSection::from_raw(edit_section);
470 let res = callback(&mut edit_section);
471
472 result_ptr.replace(res);
473 }
474 }
475
476 let trampoline_static = trampoline::<F, T>
477 as extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::EDIT_SECTION);
478
479 let mut result = None;
480 let param = Box::<CallbackParam<F, T>>::new((child_param, &mut result));
481 let param_ptr = Box::into_raw(param);
482
483 let success = unsafe {
484 ((*self.internal).call_edit_section_param)(
485 param_ptr as *mut std::ffi::c_void,
486 trampoline_static,
487 )
488 };
489
490 if success {
491 Ok(result.expect("Callback did not set result"))
492 } else {
493 Err(EditHandleError::ApiCallFailed)
494 }
495 }
496
497 pub fn get_edit_info(&self) -> crate::generic::EditInfo {
503 let mut raw_info = std::mem::MaybeUninit::<aviutl2_sys::plugin2::EDIT_INFO>::uninit();
504 unsafe {
505 ((*self.internal).get_edit_info)(
506 raw_info.as_mut_ptr(),
507 std::mem::size_of::<aviutl2_sys::plugin2::EDIT_INFO>() as _,
508 );
509 let edit_info = raw_info.assume_init();
510 crate::generic::EditInfo::from_raw(&edit_info)
511 }
512 }
513
514 pub fn restart_host_app(&self) {
516 unsafe {
517 ((*self.internal).restart_host_app)();
518 }
519 }
520
521 pub fn enumerate_effect_names<F>(&self, mut callback: F)
523 where
524 F: FnMut(Effect),
525 {
526 extern "C" fn trampoline<F>(
527 param: *mut std::ffi::c_void,
528 name: aviutl2_sys::common::LPCWSTR,
529 r#type: i32,
530 flag: i32,
531 ) where
532 F: FnMut(Effect),
533 {
534 let callback = unsafe { &mut *(param as *mut F) };
535 let name_str = unsafe { crate::common::load_wide_string(name) };
536 let effect = Effect {
537 name: name_str,
538 effect_type: EffectType::from(r#type),
539 flag: EffectFlag::from_bits(flag),
540 };
541 callback(effect);
542 }
543
544 let trampoline_static = trampoline::<F>
545 as extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR, i32, i32);
546 let user_data = &mut callback as *mut F as *mut std::ffi::c_void;
547 unsafe {
548 ((*self.internal).enum_effect_name)(user_data, trampoline_static);
549 }
550 }
551
552 pub fn get_effect_names(&self) -> Vec<Effect> {
554 let mut effects = Vec::new();
555 self.enumerate_effect_names(|effect| {
556 effects.push(effect);
557 });
558 effects
559 }
560}
561
562pub struct Effect {
564 pub name: String,
566 pub effect_type: EffectType,
568 pub flag: EffectFlag,
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq)]
574pub enum EffectType {
575 Filter,
577 Input,
579 SceneChange,
581 Other(i32),
583}
584
585define_bitflag! {
586 #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
588 #[non_exhaustive]
589 pub struct EffectFlag: i32 {
590 video: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_VIDEO,
592
593 audio: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_AUDIO,
595
596 as_filter: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_FILTER,
598 }
599}
600
601impl From<i32> for EffectType {
602 fn from(value: i32) -> Self {
603 match value {
604 0 => EffectType::Filter,
605 1 => EffectType::Input,
606 2 => EffectType::SceneChange,
607 other => EffectType::Other(other),
608 }
609 }
610}
611impl From<EffectType> for i32 {
612 fn from(value: EffectType) -> Self {
613 match value {
614 EffectType::Filter => 0,
615 EffectType::Input => 1,
616 EffectType::SceneChange => 2,
617 EffectType::Other(other) => other,
618 }
619 }
620}
621
622#[doc(hidden)]
623pub unsafe fn __internal_rwh_from_raw(
624 hwnd: aviutl2_sys::plugin2::HWND,
625 hinstance: aviutl2_sys::plugin2::HINSTANCE,
626) -> raw_window_handle::Win32WindowHandle {
627 let mut handle =
628 raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
629 handle.hinstance = Some(NonZeroIsize::new(hinstance as isize).unwrap());
630 handle
631}
632
633#[doc(hidden)]
634#[expect(private_bounds)]
635pub fn __output_log_if_error<T: MenuCallbackReturn>(result: T) {
636 if let Some(err_msg) = result.into_optional_error() {
637 let _ = crate::logger::write_error_log(&err_msg);
638 }
639}
640
641#[doc(hidden)]
642#[expect(private_bounds)]
643pub fn __alert_if_error<T: MenuCallbackReturn>(result: T) {
644 if let Some(err_msg) = result.into_optional_error() {
645 crate::common::alert_error(&anyhow::anyhow!(err_msg));
646 }
647}
648
649trait MenuCallbackReturn {
650 fn into_optional_error(self) -> Option<String>;
651}
652impl<E> MenuCallbackReturn for Result<(), E>
653where
654 Box<dyn std::error::Error>: From<E>,
655{
656 fn into_optional_error(self) -> Option<String> {
657 match self {
658 Ok(_) => None,
659 Err(e) => {
660 let boxed: Box<dyn std::error::Error> = e.into();
661 Some(format!("{}", boxed))
662 }
663 }
664 }
665}
666impl MenuCallbackReturn for () {
667 fn into_optional_error(self) -> Option<String> {
668 None
669 }
670}
671
672struct KillablePointer<T> {
673 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
674 inner: *mut T,
675}
676unsafe impl<T> Send for KillablePointer<T> {}
677unsafe impl<T> Sync for KillablePointer<T> {}
678impl<T> Drop for KillablePointer<T> {
679 fn drop(&mut self) {
680 self.kill_switch
681 .store(true, std::sync::atomic::Ordering::SeqCst);
682 }
683}
684impl<T> KillablePointer<T> {
685 pub fn new(inner: T) -> Self {
686 Self {
687 kill_switch: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
688 inner: Box::into_raw(Box::new(inner)),
689 }
690 }
691
692 pub fn create_child(&self) -> ChildKillablePointer<T> {
693 ChildKillablePointer {
694 kill_switch: std::sync::Arc::clone(&self.kill_switch),
695 inner: self.inner,
696 }
697 }
698}
699
700struct ChildKillablePointer<T> {
701 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
702 inner: *mut T,
703}
704unsafe impl<T> Send for ChildKillablePointer<T> {}
705unsafe impl<T> Sync for ChildKillablePointer<T> {}
706impl<T> ChildKillablePointer<T> {
707 pub fn is_killed(&self) -> bool {
708 self.kill_switch.load(std::sync::atomic::Ordering::SeqCst)
709 }
710
711 pub unsafe fn as_mut(&mut self) -> &mut T {
712 if self.is_killed() {
713 panic!("parent KillablePointer has been dropped");
714 }
715 unsafe { &mut *self.inner }
716 }
717}